/*
Copyright (C) 2014  Johan Degraeve

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/gpl.txt>.

*/

package helpdiabetesj2me;

import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.Font;
import javax.microedition.rms.*;

/**
 *
 * @author Johan
 */
public class AddItem extends MyCanvas implements CommandListener {
    
 
     static final String[] str_gram = {"gram"};
     static final String[] str_stuk = {"stuk"};
     static final String[] str_terug = {"Terug"};
     static final String[] str_voegtoe = {"VoegToe"};
     static final String[] str_opslaan = {"Opslaan"};
     static final String[] str_bevat = {" ==> "};
     static final String[] str_amount_kh = {"gram koolhydraten."};
     static final String[] str_andere_eenheid = {"Eenheid"};
     static final String[] str_ok = {"Ok"};

    //added 17/08/2014 - plan is that when user opens additem screen, and standard amount is not zero, then if user presses digit, standard amount is removed
    //and new amount typed by user starts from 0
    boolean amountNotYetChosen ;

    // used to pass the recid to commandAction
    int chosenrecordid;
    
    // added on 2/11/2008 to support multiple units per fooditem
    int unitNumber; //value of the unitnumber that customer has chosen to add
    int nrOfUnits; //nr of units found in the selected fooditem
    private final UnitList unitlist;
    private final String[] unitDescriptions;
    static int maxNrOfUnits = 5;

        // the user will be able to add floating point numbers as amounts.
    // the integer part (before the decimal point) will be stored in enteredstring which is inherited from MyCanvas
    // the decimal part of the amount that the user entered will be sotredin decimals
    char[] decimals;
    // maximum amount of digits in the decimal part
    public static final int maxDecimalsLength = 2;
    // maximum amount of digits in the integer part
    public static final int intpartlength = 3;
    // current length of the decimal part
    int deccurlength;
    // if true then cursor blinks on the integral part of the float and
    // user's key presses haf affect on the integral part
    // if false applicable for the decimal part'
    boolean crsorleft;
    
    //id of the record in selitemrs to be changed, if 0 means an item is added, if > 0 means an item is being changed
    int rec_to_change;
    
    
    //used in keypressed
    char[] digits = {'0','1','2','3','4','5','6','7','8','9'};

    // commands used
    private final Command Terug;
    private final Command VoegToe;
    private final Command Opslaan;
    private final Command AndereEenheid;//added 05/11 to support more units
    
    //all used in paint
    Float ins;
    String instime;
    Date today;
    Calendar cal;
    private Command ok;
    private CommandListener cmdlistner;
    private String str_chose_or_modify_amnt;
 
    Float amountOfCarbsInb;//carb amount, amount as found in b, divided by standard amount
    int standardAmountInb;

    public AddItem(Font userfont, HelpDiabetes midlet) {
        super(userfont,midlet);
        decimals = new char[maxDecimalsLength];
        unitlist = new UnitList();
        unitDescriptions = new String[maxNrOfUnits];
        
        amountOfCarbsInb = new Float();
        
        Terug = new Command(str_terug[HelpDiabetes.languageIndex],Command.EXIT,1);
        this.addCommand(Terug);
        VoegToe = new Command(str_voegtoe[HelpDiabetes.languageIndex],Command.OK,2);
        //command will be added in ItemSelected() and removed in ShowTotal()
        Opslaan = new Command(str_opslaan[HelpDiabetes.languageIndex],Command.OK,2);
        //command will be added in ItemSelected() and removed in ShowTotal()
        this.setCommandListener(this);
        //command added to support multiple units. Will be added only in case multiple units are supported, and removed otherwise
        AndereEenheid = new Command(str_andere_eenheid[HelpDiabetes.languageIndex],Command.SCREEN,3);
    }

    public void paint(Graphics graphics) {
        x=0;y=0;
        graphics.setFont(userfont);

        graphics.setColor(255,255,255);
        graphics.fillRect(0, 0, scrwidth, scrhght);
        
        //following piece of code I added because I had some problem with N73 when chosing an item from list when searching for an item
        //it appeared that sometimes , b still had the last value it had in deepsearch.paint(),
        try {
            if (chosenrecordid > 0) {
                blength = FoodFile.getRecord(chosenrecordid,b,0);
            }
        }  catch (RecordStoreNotOpenException ex) {
            (new ReportException()).Report("RecordStoreNotOpenException in AddItem.paint()",ex,false);
        } catch (InvalidRecordIDException ex) {
            (new ReportException()).Report("InvalidRecordIDException in AddItem.paint()",ex,false);
        } catch (RecordStoreException ex) {
            (new ReportException()).Report("RecordStoreException inAddItem.paint()",ex,false);
        }

        //the standard amount
        paintstring((new Integer(standardAmountInb)).toString().getBytes(),false,graphics,userfont);
        //the unit
        paintstring((" " + getUnit_in_b(unitNumber) + " ").getBytes(),false,graphics,userfont,true);
        //the food item
        paintstring(b,blength,false,graphics,userfont,true);
        //bevat
        paintstring((" " + str_bevat[HelpDiabetes.languageIndex] + " ").getBytes(),false,graphics,userfont,true);
        x=0;y= y+fonthght;

        //here I calculate and print the amount of grams of KH, per unit, if the unit is gram or milliliter then mulitply with 100, rounde to 1 digit after decimal point
        paintstring( (  Float.Int(     amountOfCarbsInb.Mul(standardAmountInb).Mul(10).Add(new Float(5,-1))   ).Div(10).toString()
                    + " " + str_amount_kh[HelpDiabetes.languageIndex]).getBytes(),false,graphics,userfont,true);
        //a new line
        x=0;y= y+fonthght;
        //an empty line
        x=0;y= y+fonthght;

        if (enteredstring.enteredstringlength > 0) {
            //paint the integer part
            paintstring((new String(enteredstring.enteredstring)).getBytes(),enteredstring.enteredstringlength,true,graphics,userfont,false);
        } else {
            // paint '0'
            paintstring("0".getBytes(),true,graphics,userfont,false);
        }
        if (crsorleft) {
            printcursor(graphics, true);
            x=x+2;
        }
        // print the decimal character
        paintstring(".".getBytes(),true,graphics,userfont,false);
        if (deccurlength > 0) {
            paintstring((new String(decimals)).getBytes(),deccurlength, true,graphics,userfont,false);
            if (!crsorleft) {
                printcursor(graphics, true);
                x=x+2;
            }
        } else {
            // paint '0'
            if (!crsorleft) {
                printcursor(graphics, true);
                x=x+2;
            }
            paintstring("0".getBytes(),true,graphics,userfont,false);
        }

        x=x+userfont.charWidth(' ');
        paintstring((  getUnit_in_b(unitNumber) + " " + str_bevat[HelpDiabetes.languageIndex] + " ").getBytes(),false,graphics,userfont,true);
        x=0;y= y+fonthght;

        if (!amountOfCarbsInb.isError()) {
            paintstring(       (((Float.Int(CalcAmount().Mul(amountOfCarbsInb).Mul(10).Add(new Float(5,-1))).Div(10).toString())) + " " + str_amount_kh[HelpDiabetes.languageIndex]).getBytes(),false,graphics,userfont,true);
            newline(0);
        }
        //reset the screensize to it's real values
        scrwidth = midlet.getDisplay().getCurrent().getWidth();

    }

    public void commandAction(Command command, Displayable displayable) {
        resetexittimer();
        if (command == VoegToe) {
            addItemToList(chosenrecordid,CalcAmount(),unitNumber,rec_to_change);
            //switch back to getitemfromuser - don't switch back to curdisp because curdisp may be DeepSearch' - it's better to switch hback immediately to getitemfromuser'
            midlet.getDisplay().setCurrent(HelpDiabetes.getitemfromuser);
        } else if (command == Opslaan){
            addItemToList(chosenrecordid,CalcAmount(),unitNumber,rec_to_change);
            midlet.getDisplay().setCurrent(curdisp);
        } else if (command == AndereEenheid){
            unitlist.selectUnit(this,nrOfUnits,unitDescriptions);
        }  else {
            midlet.getDisplay().setCurrent(curdisp);
        }
        resetflipfloptimer(curdisp);
    }
    
    public synchronized void keyPressed (int keyCode) {
        resetexittimer();
      
        cancelflipfloptimer();
        
        //first check for num values, this has highest priority
        //then for gameactions
        //then back or clearkey
            //checking on value -15000 first because otherwise a call woud be method to getgameaction(-15000) which gave an exception with the emulator
            if (keyCode == -15000) {//-15000 ued for a trick, see comment a bit further
                        movebackthecursor();
                }         
            else if (        (keyCode > (KEY_NUM0 -1)) && (keyCode < (KEY_NUM9 + 1))       ) {
                if (amountNotYetChosen) {
                    deccurlength = 0;
                    enteredstring.enteredstringlength=0;
                    amountNotYetChosen = false;
                }
                if (crsorleft) {
                    // try to add a digit on the integer part
                    if (enteredstring.enteredstringlength < intpartlength) {
                        // we can still add something
                        //if enteredstring.enteredstring = "0" then I'll remove it and reset the length to 0'
                        if (enteredstring.enteredstringlength == 1 & enteredstring.enteredstring[0] == '0') {
                            enteredstring.enteredstring[0]=digits[keyCode - KEY_NUM0];
                        } else {
                            enteredstring.enteredstring[enteredstring.enteredstringlength] = digits[keyCode - KEY_NUM0];
                            enteredstring.enteredstringlength++;
                        }
                    } // else we do nothing
                } else {
                    // try to the decimal part
                    if (deccurlength < maxDecimalsLength) {
                        // we still can add something
                        decimals[deccurlength] = digits[keyCode - KEY_NUM0];
                        deccurlength++;
                    } // else we do nothing
                }
            } else
            if (getGameAction(keyCode) == LEFT) {
                    if (!crsorleft) {
                        if (deccurlength > 0) {
                            //here we'll use the LEFT key to remove a character, and not to jump the cursor to the right part'
                            // so I recall keypressed with -15000, which is my own keycode to force a back key
                            keyPressed(-15000);
                            //now if there's no more digits on the right side we'll also jump the cursor to the left side'
                            if (deccurlength  == 0) {
                                crsorleft = true;
                            }
                        } else {
                            crsorleft = true;
                        }
                    } else {//cursor is on the left side
                        if (enteredstring.enteredstringlength > 0) {
                            //here we'll use the LEFT key to remove a character, and not to jump the cursor to the right part'
                            // so I recall keypressed with -15000, which is my own keycode to force a back key
                            keyPressed(-15000);
                        } else {
                            crsorleft =true;
                        }    
                    }
            } else if (getGameAction(keyCode) == RIGHT) {
                    if (crsorleft) {
                          crsorleft = false;
                    }
             //switch back to getitemfromuser - don't switch back to curdisp because curdisp may be DeepSearch' - it's better to switch hback immediately to getitemfromuser'
            } else if (getGameAction(keyCode) == FIRE) {
                addItemToList(chosenrecordid,CalcAmount(),unitNumber,rec_to_change);
                if (rec_to_change > 0) {//switch back to readitemlist
                    midlet.getDisplay().setCurrent(curdisp);
                } else {
                    //switch back to getitemfromuser - don't switch back to curdisp because curdisp may be DeepSearch' - it's better to switch hback immediately to getitemfromuser'
                    midlet.getDisplay().setCurrent(HelpDiabetes.getitemfromuser);
                }
            } else if (keyCode == BACK_KEY | keyCode == CLEAR_KEY | keyCode == -15000) {//-15000 ued for a trick, see comment a bit further
                        movebackthecursor();
                } 
        if (keyCode != -15000) {
            flipflopthecursor();
        }
    }
    
    //just a method because it's a piece of code that I need twice'
    void movebackthecursor() {
        if (crsorleft) {
            if (enteredstring.enteredstringlength > 0) {
                enteredstring.enteredstringlength--;
                enteredstring.enteredstring[enteredstring.enteredstringlength] = ',';
            }
        } else {
            if (deccurlength > 0) {
                deccurlength--;
                decimals[deccurlength] = ',';
            } // else we do nothing
        }
    }
    
    /**
     * the method that will be called by an instance of getitemfrom user when user has selected an item, or an instance of ReadItemList
     * 
     * @param foodrs_recid point the the food element in foodrs
     * @param newItem  true means it's a new item being added, false means it's an existing item being changed via readitemlist
     * @param oldamount the amount of units in the selected record, only applicable in case called for changing an existing item - it's in string formatted float'
     * @param selitemrs_recid points to the record in selitemsrs to be changed
     * @param unitNumber the newItem number in the record
     */
    public void ItemSelected (int foodrs_recid, boolean newItem, String oldamount, int selitemrs_recid, int unitNumber) {
        curdisp = midlet.getDisplay().getCurrent();
        ItemSelectedWithoutStoringCurdisp(foodrs_recid,newItem,oldamount,selitemrs_recid,unitNumber);
    }

        /**
     * similar to ItemSelectedWithoutStoringCurdisp but this one stores the current display
     * 
     * @param foodrs_recid point the the food element in foodrs
     * @param newItem  true means it's a new item being added, false means it's an existing item being changed via readitemlist
     * @param oldAmount the amount of units in the selected record, only applicable in case called for changing an existing item - it's in string formatted float'
     * @param selitemrs_recid points to the record in selitemsrs to be changed
     * @param unitNumber the newItem number in the record
     */
    public void ItemSelectedWithoutStoringCurdisp (int foodrs_recid, boolean newItem, String oldAmount, int selitemrs_recid, int unitNumber) {
        int i2;
        int i1;
        Float amountToUse;
        amountToUse = Float.parse(oldAmount,10);
        this.unitNumber = unitNumber;
        
        //added 17/08 - plan is that when user opens additem screen, and standard amount is not zero, then if user presses digit, standard amount is removed
        //and new amount typed by user starts from 0
        amountNotYetChosen = true;
        
        try {
            if (newItem) {// means the method is called to add a new element.
                this.addCommand(VoegToe);
                this.removeCommand(Opslaan);
            } else {
                this.addCommand(Opslaan);
                this.removeCommand(VoegToe);
            }
            
            midlet.getDisplay().setCurrent(this);
            resetflipfloptimer(this);
            
            chosenrecordid = foodrs_recid;
            blength = FoodFile.getRecord(foodrs_recid,b,0);
            
            // count number of units in the selected food item
            //if the selected item contains multiple units, then we'll add the command which allows to select the newItem'
            nrOfUnits = 1;
            i1 = getfirstsemicolon();
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                unitDescriptions[0] = new String(b,i1+1,i2-i1-1);//which is from i1 to i2 minus the comma to which i2 is pointing
                i1=i2;
                i2=getnextsemicolon();
                unitDescriptions[0] = new String(b,i1+1,i2-i1-1) + " " + unitDescriptions[0];
            } else
            {
                getnextsemicolon();
                unitDescriptions[0] = "1 " + str_stuk[HelpDiabetes.languageIndex] ;
            }
            getnextsemicolon();getnextsemicolon(); getnextsemicolon();
            i1 = getnextsemicolon();
            try { // aim is to continue as long as there's no exception. If an exception occurs it means there's no furtehr newItem'
                if (i1 == -1) throw new Exception();
                i2 =  getnextsemicolon();
                while (nrOfUnits < (maxNrOfUnits +1)) {
                    if (i2 > i1 + 1) {//trying to read the newItem description although it could be that the end of the line is already passed
                       unitDescriptions[nrOfUnits]= new String(b,i1+1,i2-i1-1);
                       i1=i2;
                       i2=getnextsemicolon();
                       unitDescriptions[nrOfUnits]= new String(b,i1+1,i2-i1-1) + " " + unitDescriptions[nrOfUnits];
                    } else
                    {
                       i2=getnextsemicolon();
                        unitDescriptions[nrOfUnits] = "1 " + str_stuk[HelpDiabetes.languageIndex];
                    }
                    nrOfUnits = nrOfUnits + 1;
                    i1 = getnextsemicolon();
                    if (i1 == -1) {
                        nrOfUnits = nrOfUnits - 1;
                        throw new Exception();
                    }
                    i1 = getnextsemicolon();
                    if (i1 == -1) {
                        nrOfUnits = nrOfUnits - 1;
                        throw new Exception();
                    }
                    i2 = getnextsemicolon();
                    if (i2 == -1) {
                        nrOfUnits = nrOfUnits - 1;
                        throw new Exception();
                    }
                    if (i2 > i1 + 1) {
                    } else {
                        //there's no amount filled in
                        nrOfUnits = nrOfUnits - 1;
                        throw new Exception();
                    }
                    // there was a unit2
                    i1 = getnextsemicolon();
                    i2 = getnextsemicolon();
                    if (i1 == -1 || i2 == -1) {
                        throw new Exception();
                    }
                }
            } catch (Exception e) {
                
            }
            if (nrOfUnits > 1) {
                this.addCommand(AndereEenheid);
            } else {
                this.removeCommand(AndereEenheid);
            }

            standardAmountInb = CalculateStandardAmount_in_b(unitNumber);
            amountOfCarbsInb = CalculateCarbAmount_in_b(unitNumber).Div(standardAmountInb);
             
            // this I do because i want to call paintstring(byte[],boolean,graphics) with entredstring.enteredstring
            // but only the entered digits should be printed, so as user adds or removes digits, ',' will be added or removed (see keypressed)
            int i;
            for (i = 0 ; i<enteredstring.enteredstring.length;i++) {
                enteredstring.enteredstring[i] =',';
            }
            for (i = 0;i<decimals.length;i++) {
                decimals[i] = ',';
            }
            
            if (newItem) {
                amountToUse = new Float(standardAmountInb);
            }
            
            //following settings assume method is called for adding a new item
            deccurlength = 0;
            enteredstring.enteredstringlength=0;
            char[] strval = amountToUse.toString().toCharArray();
            for (i = 0;i < strval.length;i++) {
                if (strval[i] == '.') {
                    break;
                }
                enteredstring.enteredstring[i] = strval[i];
                enteredstring.enteredstringlength = i+1;
            }
            i++;
            for (;i < strval.length;i++) {
                decimals[i - (enteredstring.enteredstringlength + 1)] = strval[i];
                deccurlength = i - enteredstring.enteredstringlength;
                if ((deccurlength == 1) & (decimals[0] == '0')) {
                    deccurlength = 0;
                    decimals[0] = ',';
                }
            }

            rec_to_change = 0;
            if (!newItem) {
                rec_to_change = selitemrs_recid ;
            } 

            crsorleft = true;
            //we're ready to print the stuff and let the user chose how much he wants'
            repaint();

            //open newItem screen immediately after having selected an item.
            if (nrOfUnits > 1 && newItem)
                commandAction(AndereEenheid, this);
              
        } catch(NumberFormatException nex) {
            midlet.getDisplay().setCurrent(curdisp);
            (new UserInfo()).Report("Error\n\n" 
                    + "This item is not correctly formatted.\n"
                    + "Check the excel food table in row " + chosenrecordid);
        } catch (RecordStoreException ex) {
            midlet.getDisplay().setCurrent(curdisp);//setting back to getitemfrom user, report exception will immediately set it's own displayable and switch back to getitem from user after user pressed ok'
            (new ReportException()).Report("Exception in AddItem.ItemSelected while processing record " + foodrs_recid,ex,false);
        }
    }
    
    // this method will return enteredstring into a float value
    Float CalcAmount() {
        return Float.parse(
        ((enteredstring.enteredstringlength > 0) 
            ? (new String(enteredstring.enteredstring,0,enteredstring.enteredstringlength))
            : ("0") )
            +
         "."
            +
         ((deccurlength > 0)
            ? (new String(decimals,0,deccurlength))
            : ("0"))
        ,10 );
    }
    
    
   //adds an item with foodfile record id, amount and unit to the list selitemsrs
   // recid = id of the record in foodrs which contains the added item
   // amount = amount chosen by user
    // unitnumber = added on 2/11/2008 to allow multiple units per food item
   // rec_to_change : if > 0 then it's an existing record in selitemsrs that needs to be changed'
   void addItemToList (int recid, Float amount, int unitnumber, int rec_to_change) {
       byte [] oldb;
       try {
           oldb = (Integer.toString(recid) + "," + amount.toString() + "," + Integer.toString(unitnumber) + "," + Long.toString((new Date()).getTime())).getBytes() ;
           if (rec_to_change == 0) {
                HelpDiabetes.getSelectedItemRs().addRecord(oldb,0,oldb.length);
           } else {
               HelpDiabetes.getSelectedItemRs().setRecord(rec_to_change,oldb,0,oldb.length);
           }
           HelpDiabetes.getitemfromuser.emptyenteredstring();
        }  catch (RecordStoreException ex2) {
            (new ReportException()).Report("Exception in AddItem.addItemToList",ex2,true);
        }
   }
   
   //returns the unit_number_to-add
   public int get_unit_nubmer_to_add() {
       return unitNumber;
   }
   
   //returns the rec-to-chnage
   public int get_rec_to_change() {
       return rec_to_change;
   }
   
   //returns the chosen_rec_id
   public int get_chosen_record_id() {
       return chosenrecordid;
   }

   String GetClassName() {
        return("AddItem");
   }

}
